﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Ricciolo.StylesExplorer.MarkupReflection;
using System.Xml;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Xml.XPath;
using System.Linq;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Diagnostics;
using System.Collections;
using System.Windows.Controls.Primitives;
using System.Threading;

namespace Ricciolo.StylesExplorer.Forms
{
    public partial class PreviewZone : System.Windows.Forms.Form
    {
        public PreviewZone()
        {
            InitializeComponent();
        }

        private UIElement currentElement;
        private KnownInfo knownInfo = new KnownInfo();

        protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaintBackground(e);

            // Draw gradient background
            using (System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(this.ClientRectangle, System.Drawing.SystemColors.Control, System.Drawing.SystemColors.Window, 270))
            {
                e.Graphics.FillRectangle(brush, e.ClipRectangle);
            }
        }

        /// <summary>
        /// Start message loop
        /// </summary>
        public void Start()
        {
            System.Windows.Forms.Application.Run(this);
        }

        /// <summary>
        /// Stop message loop
        /// </summary>
        public void Stop()
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action(Stop));
                return;
            }

            this.elementHost.Child = null;
            this.Close();
            System.Windows.Forms.Application.Exit();

            ManualResetEvent mrs = new ManualResetEvent(false);
            System.Windows.Forms.Application.ApplicationExit += delegate
            {
                mrs.Set();
            };
            mrs.WaitOne();
        }

        public void Resize(System.Drawing.Size newSize)
        {
            this.BeginInvoke(new Action<System.Drawing.Size>(delegate(System.Drawing.Size size)
                {
                    this.Location = new System.Drawing.Point(0, 0);
                    this.Size = size;
                }), newSize);
        }

        public void SetXaml(string xaml)
        {
            // Ensure call preview on main preview thread
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action<String>(SetXaml), xaml);
                return;
            }

            using (XmlReader rawReader = new XmlTextReader(new System.IO.StringReader(xaml)))
            {
                try
                {
                    this.elementHost.Child = null;

                    // Preparo il documento
                    IXPathNavigable data = PrepareDocument(rawReader);

                    // Parser it
                    using (XmlReader reader = data.CreateNavigator().ReadSubtree())
                    {
                        object root = XamlReader.Load(reader);

                        this.currentElement = GetElement(root);
                        this.elementHost.Child = currentElement;

                        //this.ApplyResingAdorner(resizeModeCheck.IsChecked.Value);
                    }
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());

                    // Show error
                    TextBox tb = new TextBox();
                    tb.Text = e.Message;
                    tb.BorderThickness = new Thickness(0);
                    tb.TextWrapping = TextWrapping.Wrap;
                    tb.IsReadOnly = true;
                    this.elementHost.Child = tb;
                }
            }
        }

        private UIElement GetElement(object oroot)
        {
            object root;

            ResourceDictionary rd = oroot as ResourceDictionary;
            if (rd != null)
            {
                if (rd.Count == 0)
                    return null;
                else
                    root = rd[this.GetHashCode().ToString()];
            }
            else
                root = oroot;

            if (root is UIElement)
                return (UIElement)root;
            else if (root is Brush)
            {
                Rectangle r = GetRectangleSample();
                r.Fill = (Brush)root;
                return r;
            }
            else if (root is Geometry)
            {
                Path p = new Path();
                p.Fill = SystemColors.HighlightBrush;
                p.Data = (Geometry)root;
                return p;
            }
            else if (root is Drawing)
            {
                Rectangle r = GetRectangleSample();
                r.Fill = new DrawingBrush((Drawing)root);
                return r;
            }
            else if (root is Style)
            {
                Style style = (Style)root;
                DependencyObject d = GetFrameworkElementSample(style.TargetType);
                if (d is FrameworkElement)
                {
                    FrameworkElement fe = (FrameworkElement)d;
                    fe.Style = style;
                    fe.ApplyTemplate();

                    // Imposto una dimensione se non c'è
                    if (Double.IsNaN(fe.Width))
                        fe.Width = 300;
                    if (Double.IsNaN(fe.Height))
                        fe.Height = 150;

                    return fe;
                }
                else if (d is FrameworkContentElement)
                {
                    FrameworkContentElement fe = (FrameworkContentElement)d;
                    fe.Style = style;
                    // TODO: 
                }

                return null;
            }

            ContentControl content = new ContentControl();
            content.Content = root;
            return content;
        }

        private DependencyObject GetFrameworkElementSample(Type type)
        {
            DependencyObject fe = (DependencyObject)Activator.CreateInstance(type);
            if (fe is ItemsControl)
            {
                ItemsControl itemsControl = (ItemsControl)fe;
                itemsControl.Items.Add("Sample item 1");
                itemsControl.Items.Add("Sample item 2");
                itemsControl.Items.Add("Sample item 3");
                itemsControl.Items.Add("Sample item 4");
                itemsControl.Items.Add("Sample item 5");

                if (fe is HeaderedItemsControl)
                {
                    ((HeaderedItemsControl)fe).Header = "Sample header";
                }
            }
            else if (fe is IAddChild)
                ((IAddChild)fe).AddText("Sample content");

            if (fe is RangeBase)
            {
                ((RangeBase)fe).Minimum = 0;
                ((RangeBase)fe).Value = 50;
                ((RangeBase)fe).Maximum = 100;
            }

            if (fe is HeaderedContentControl)
                ((HeaderedContentControl)fe).Header = "Sample header";

            return fe;
        }

        private static Rectangle GetRectangleSample()
        {
            Rectangle r = new Rectangle();
            r.Width = 100;
            r.Height = 100;
            return r;
        }

        private IXPathNavigable PrepareDocument(XmlReader reader)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(reader);

            XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
            nsManager.AddNamespace("p", XmlPIMapping.PresentationNamespace);
            nsManager.AddNamespace("x", XmlPIMapping.XamlNamespace);

            // Rimuovo la chiave se è sulla root
            XmlAttribute keyNode = doc.SelectSingleNode("/*[1]/*[last()]/@x:Key", nsManager) as XmlAttribute;
            if (keyNode != null)
                keyNode.Value = this.GetHashCode().ToString();
            keyNode = doc.SelectSingleNode("/*[1]/@x:Key", nsManager) as XmlAttribute;
            if (keyNode != null)
                keyNode.OwnerElement.Attributes.Remove(keyNode);

            XmlNodeList list = doc.SelectNodes("/*[1]/*/@x:Key", nsManager);
            List<String> keys = new List<String>();
            // Remove duplicated keys
            foreach (XmlAttribute keyAttribute in list)
            {
                if (keys.Contains(keyAttribute.Value))
                    keyAttribute.OwnerElement.ParentNode.RemoveChild(keyAttribute.OwnerElement);
                else
                    keys.Add(keyAttribute.Value);
            }

            // Remove unknown type
            if (doc.DocumentElement.NamespaceURI != XmlPIMapping.PresentationNamespace || !knownInfo.IsKnownType(doc.DocumentElement.LocalName))
            {
                XmlElement newRoot = doc.CreateElement(String.Empty, "ContentControl", XmlPIMapping.PresentationNamespace);
                foreach (XmlNode node in doc.DocumentElement.ChildNodes)
                    newRoot.AppendChild(node.CloneNode(true));
                doc.RemoveAll();
                doc.AppendChild(newRoot);
            }

            return doc;
        }
    }
}
